#pragma once

#include <iostream>
#include <functional>
#include <unordered_map>
#include <cassert>
#include "Sock.hpp"
#include "Epoller.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

namespace tcp_server
{
    static const int default_port = 8080;
    static const int default_ev_num = 128;

    class Connection; // 声明
    class TcpServer;  // 声明

    using func_t = std::function<void(Connection *)>;
    // using handle_t = std::function<void(const std::string& package)>;

    /* 多路转接下，接收端可能一次并不能读取一个完整的报文
        ET模式下，即使保证了每轮都能把数据读完，但也不能保证读取到的是一个完整的报文
       所以需要将读取到的数据暂存在缓冲区内，且这个缓冲区需要是每个套接字自己的缓冲区
       所以，套接字要有自己的空间 */
    class Connection
    {
    public:
        Connection(int sock, TcpServer *tsp) : _sock(sock), _tsp(tsp)
        {
            // _lasttime = time(nullptr);
        }

        void Register(func_t r, func_t s, func_t e)
        {
            _recver = r;
            _sender = s;
            _excepter = e;
        }

        void Close()
        {
            close(_sock);
        }

        ~Connection()
        {
        }

    public:
        int _sock;
        std::string _inbuffer;  // 接收缓冲区
        std::string _outbuffer; // 发送缓冲区

        func_t _recver;   // 从sock中读
        func_t _sender;   // 向sock中写
        func_t _excepter; // 处理sock IO时上面的异常事件

        TcpServer *_tsp; // 回指指针，指向TcpServer  可省略

        // uint64_t _lasttime;// 最近访问时间，时间戳
    };

    class TcpServer
    {
    public:
        TcpServer(func_t func, const uint16_t port = default_port)
            : _port(port), _revs(nullptr), _ev_num(default_ev_num), _service(func)
        {
        }

        void InitServer()
        {
            /* 1. 创建socket */

            // 创建套接字
            _sock.Socket();
            // 绑定端口
            _sock.Bind(_port);
            // 设置为监听状态
            _sock.Listen();

            /* 2. 构建epoll */
            _epoller.Create();

            /* 3. 将目前唯一的一个套接字即listen套接字交付给epoll */

            // 为每一个socket（实际只有listen套接字）设置为ET模式，并添加Connetion，设置事件回调方法
            AddConnetion(_sock.Fd(), EPOLLIN | EPOLLET,
                         std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);

            // 初始化就绪事件数组
            _revs = new struct epoll_event[_ev_num];
        }
        /* 派发事件 */
        void Dispatch()
        {
            int timeout = -1; // ms

            while (true)
            {
                Loop(timeout);
                // logMessage(DEBUG, "time out ...");

                // // 遍历所有Connection，计算每一个Connection已经有多久没有动静了
                // uint64_t curtime = time(nullptr);
                // for(auto& e : _connections)
                // {
                //     if(e.first == _sock.Fd()) continue;
                //     uint64_t standard_timedif = 5 * 60;
                //     uint64_t timedif = curtime - e.second->_lasttime;
                //     logMessage(DEBUG, "lasttime: %ld, 多久未访问：%ld", e.second->_lasttime, timedif);
                //     if(timedif > standard_timedif) Close(e.second);
                // }
            }
        }

        /* 开启指定套接字对读写事件的关心 */
        void EnableReadWrite(Connection *conn, bool readable, bool writeable)
        {
            uint32_t event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
            _epoller.Control(conn->_sock, event, EPOLL_CTL_MOD);
        }

        ~TcpServer()
        {
            _sock.Close();
            _epoller.Close();
            if (_revs)
                delete[] _revs;
            // TODO
        }

    private:
        void AddConnetion(int sock, uint32_t events, func_t recver, func_t sender, func_t excepter)
        {
            /* 1. 为sockfd创建Connection，并初始化，添加到_connections中 */
            // ET模式下设置文件描述符为非阻塞
            if (events & EPOLLET)
                Util::SetNonBlock(sock);
            // assert(f);
            // 创建Conection
            Connection *conn = new Connection(sock, this);

            /* 2. 给对应的sock创建对应的回调方法 */
            conn->Register(recver, sender, excepter);

            /* 2. 将sock与其要关心的事件“写透式”注册到epoll中，让epoll帮我们关心事件 */
            bool f = _epoller.AddEvent(sock, events);
            assert(f);
            (void)f;

            /* 3. 将<sock, conn>添加到_connections中 */
            _connections.insert({sock, conn}); // C++11
            // _connections.insert(std::pair<int, Connection*>(sock, conn));

            logMessage(DEBUG, "add new socket: %d into epoll and unordered_map", sock);
        }
        /* listen套接字 读事件处理 */
        void Accepter(Connection *conn)
        {
            // listen套接字被设置为了ET模式，且是非阻塞式IO，所以事件就绪只会通知一次，Accpter同样如此，\
            但是，我们不能保证底层到来的连接只有一个，所以就倒逼我们程序员必须把所有连接都获取，所以这里需要是循环
            while (true)
            {
                // 获取新连接
                std::string clientip;
                uint16_t clientport;
                int err_code = 0;
                int sockfd = _sock.Accept(&clientip, &clientport, &err_code);
                if (sockfd > 0)
                {
                    // 为sockfd配置Connnection
                    AddConnetion(sockfd, EPOLLIN | EPOLLET,
                                 std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                                 std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                                 std::bind(&TcpServer::Excepter, this, std::placeholders::_1));
                    logMessage(NORMAL, "get a new link from %s:%d", clientip.c_str(), clientport);
                }
                else
                {
                    if (err_code == EAGAIN) // 底层没有数据
                        break;
                    else if (err_code == EINTR) // 信号中断
                        continue;
                    else
                        break;
                }
            }

            logMessage(DEBUG, "Accepter called");
        }
        /* 读事件处理 */
        void Recver(Connection *conn)
        {
            // conn->_lasttime = time(nullptr);// 更新最近访问时间

            /* 非阻塞的将接收到的数据放到接收缓冲区，然后进行数据处理和业务处理 */
            logMessage(DEBUG, "Recver called");
            char buffer[1024];
            while (true)
            {
                ssize_t s = recv(conn->_sock, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s] = 0;             // 当作字符串
                    conn->_inbuffer += buffer; // 将读取到的数据入队列
                }
                else if (s == 0)
                {
                    if (conn->_excepter)
                    {
                        conn->_excepter(conn); // 对方关闭连接，当异常处理，因为代码中会存在大量的异常处理，所以这里将其当为异常处理
                        return;                // 注意！！！这里直接退出，不然下次循环会进入到下一个else分支中
                    }
                }
                else
                {
                    if (errno == EAGAIN)
                        break; // 底层没数据
                    else if (errno == EINTR)
                        continue; // 信号中断
                    else          // 出现异常
                        if (conn->_excepter)
                        {
                            conn->_excepter(conn);
                            return;
                        }
                }
            }

            logMessage(DEBUG, "sock:%d -> %s", conn->_sock, conn->_inbuffer.c_str());

            /* 数据处理 */
            _service(conn); // 服务器只处理IO，其他业务逻辑则不关心
            logMessage(DEBUG, "Recver end called");

            // /* 解析数据，得到完整报文 */
            // std::string one_package;
            // while(!ParseOnePackage(conn->_inbuffer, &one_package))
            // {
            //     // 走到这里，得到一个完整报文
            //     // 将报文向上交付
            //     _handle(one_package);
            // }
        }
        /* 写事件处理 */
        void Sender(Connection *conn)
        {
            // conn->_lasttime = time(nullptr);// 更新最近访问时间

            /* 非阻塞的将发送缓冲区中的数据发送出去 */
            while (true)
            {
                ssize_t s = send(conn->_sock, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
                if (s > 0)
                {
                    if (conn->_outbuffer.empty())
                    {
                        // EnableReadWrite(conn, true, false); // 发送完了，关闭对写事件的关心
                        break;
                    }
                    else
                        conn->_outbuffer.erase(0, s); // 从缓冲区中清掉已经发送的数据
                }
                else
                {
                    if (errno == EAGAIN)
                        break; // 缓冲区没数据
                    else if (errno == EINTR)
                        continue; // 信号中断
                    else          // 出错
                    {
                        if (conn->_excepter)
                        {
                            conn->_excepter(conn);
                            return;
                        }
                    }
                }
            }

            // 没有发送完毕，就要对对应的socket开启对写事件的关心
            if (!conn->_outbuffer.empty())
                conn->_tsp->EnableReadWrite(conn, true, true);
            // 发送完毕，就要关闭对对应的socket关闭对写事件的关心
            else
                conn->_tsp->EnableReadWrite(conn, true, false);
        }

        void Close(Connection* conn)
        {
            // 先将socket从epoll中移除
            _epoller.Control(conn->_sock, 0, EPOLL_CTL_DEL);
            // 再关闭套接字
            conn->Close();
            // 然后移除套接字关联的Connection
            _connections.erase(conn->_sock); // 这里只是删除<k,v>关系，但Connection实体并没有被删除

            logMessage(DEBUG, "except had handled, all source of sock: %d had been recycled", conn->_sock);

            // 删除Connection实体
            delete conn;
        }
        /* 异常事件处理 */
        void Excepter(Connection *conn)
        {
            logMessage(DEBUG, "except appear");

            // // 先将socket从epoll中移除
            // _epoller.Control(conn->_sock, 0, EPOLL_CTL_DEL);
            // // 再关闭套接字
            // conn->Close();
            // // 然后移除套接字关联的Connection
            // _connections.erase(conn->_sock); // 这里只是删除<k,v>关系，但Connection实体并没有被删除

            // logMessage(DEBUG, "except had handled, all source of sock: %d had been recycled", conn->_sock);

            // // 删除Connection实体
            // delete conn;

            Close(conn);
        }
        bool IsConnectionExists(int sockfd)
        {
            auto it = _connections.find(sockfd);
            return it != _connections.end();
        }
        /* 获取已经就绪的事件 */
        void Loop(int timeout)
        {
            int n = _epoller.Wait(_revs, _ev_num, timeout);
            logMessage(DEBUG, "wait_n: %d", n);
            for (int i = 0; i < n; ++i)
            {
                int sockfd = _revs[i].data.fd;     // 获取就绪事件所属文件
                uint32_t events = _revs[i].events; // 获取就绪事件集

                logMessage(DEBUG, "正在处理socket[%d]上的就绪事件", sockfd);

                /* 派发事件 */

                // 将所有的异常问题都转化为读写问题。读写本来就有异常问题，只要读写把异常处理了，这里的异常也就被处理了
                if (events & EPOLLERR)
                    events |= (EPOLLIN | EPOLLOUT);
                if (events & EPOLLHUP)
                    events |= (EPOLLIN | EPOLLOUT);

                // 读事件就绪
                if ((events & EPOLLIN) && IsConnectionExists(sockfd) && _connections[sockfd]->_recver) // 保证Connection存在且回调方法已被设置
                    _connections[sockfd]->_recver(_connections[sockfd]);                               // 不同套接字就调用自己绑定的相应处理方法，\
                            如listen套接字绑定的是Accepter
                // 写事件就绪
                else if ((events & EPOLLOUT) && IsConnectionExists(sockfd) && _connections[sockfd]->_sender)
                    _connections[sockfd]->_sender(_connections[sockfd]);
            }
        }

    private:
        Sock _sock;
        uint16_t _port;
        Epoller _epoller;

        std::unordered_map<int, Connection *> _connections; // <sock, Connection>

        struct epoll_event *_revs; // 等待成功的就绪事件
        int _ev_num;               // 就绪事件的个数

        // handle_t _handle;
        func_t _service;
    };
}